import {ChangeDetectorRef, Component} from '@angular/core';
import {DocService} from '../../service/doc.service';
import {ActivatedRoute, Router} from '@angular/router';
import {PageVoService} from '../../service/page-vo.service';
import {EnvItem} from '../../entity/env-item';
import {RequestParam} from '../../entity/request-param';
import {RequestBodyDto} from '../../entity/request-body-dto';
import {InvokeUtils} from '../../utils/invoke-utils';
import {SendService} from '../../service/send.service';
import {Observable} from 'rxjs/index';
import {InvokeResultDto} from '../../entity/invoke-result-dto';
import {BaseResponse} from '../../entity/base-response';
import {UserService} from '../../service/user.service';
import {User} from '../../entity/user';
import {MessageService} from '../../service/message.service';
import {AlertMessage} from '../../entity/alert-message';
import {TpHttpService} from '../../service/tp-http.service';
import {BaseApiComponent} from '../page-api/base-api.component';
import {InvokeDataRest} from '../../entity/invoke-data-rest';
import {Menu} from '../../entity/menu';
import {deepCopy, EntityUtils} from '../../utils/entity-utils';
import {ArrayUtils} from '../../utils/array-utils';

@Component({
    selector: 'app-page-invoke',
    templateUrl: './page-invoke.component.html',
    styleUrls: ['./page-invoke.component.css']
})
export class PageInvokeComponent extends BaseApiComponent {
    activeTab = 'params';

    data: InvokeDataRest;

    demos: InvokeDataRest[];

    actions = ['Send', 'Code'];

    publicStates = InvokeUtils.getPublishStatus();

    actionName = 'Code';

    invokeResult: InvokeResultDto;

    user: User;

    /**
     * 是否显示保存对话框
     * @type {boolean}
     */
    showSaveDialog = false;

    /**
     * 正在编辑中的配置
     */
    editedData = {
        id: null,
        publicState: false,
        name: ''
    };

    private paramMap: Map<string, RequestParam>;

    constructor(protected ref: ChangeDetectorRef,
                protected pageVoService: PageVoService,
                protected activatedRoute: ActivatedRoute,
                protected router: Router,
                protected docService: DocService,
                private sendService: SendService,
                private userService: UserService,
                private httpService: TpHttpService,
                protected messageService: MessageService) {
        super(pageVoService, activatedRoute, router, docService, messageService);
    }

    protected onApiReady() {
        if (!this.api) {
            this.messageService.add(AlertMessage.error('接口不存在或已被删除！', 'api-error'));
            return;
        }
        if (!this.api.methods) {
            this.api.methods = ['GET', 'POST'];
        }

        const invokeData = new InvokeDataRest();
        invokeData.docId = this.docId;
        invokeData.apiKey = this.api.key;
        invokeData.env = this.env.restEnvs[0];
        invokeData.pathParams = [];
        invokeData.headers = RequestParam.getDefaultHeaders();
        invokeData.params = [];
        invokeData.name = '默认';

        invokeData.requestBody = RequestBodyDto.getDefault();
        invokeData.method = this.api.methods[0];
        this.actionName = this.actions[0];

        this.userService.userObservable.subscribe((user: User) => {
            if (user && user.name) {
                this.user = user;
            } else {
                // 跳转回去
                this.router.navigate(['doc', `${this.doc.artifact.groupId}:${this.doc.artifact.artifactId}`, this.api.key]);
            }
        });

        // 初始化参数
        this.activeTab = 'params';
        this.invokeResult = null;
        const requestParams = this.api.requestParams;
        if (requestParams) {
            requestParams.forEach(param => {
                if (param.annotation === '@PathVariable') {
                    const pathParam: RequestParam = <RequestParam>{
                        key: param.name,
                        required: true,
                        value: param.demo,
                        description: param.comment
                    };
                    InvokeUtils.setPathParamPattern(pathParam, this.api.paths[0]);
                    invokeData.pathParams.push(pathParam);
                    this.activeTab = 'pathParams';
                } else if (param.annotation === '@RequestParam') {
                    // 检查是否复杂体，如果是，需要展开
                    const entity = this.docService.getEntity(param.entityName);
                    if (EntityUtils.isPrimitive(entity) || entity.enumerate) {
                        // 简单类型，直接使用
                        InvokeUtils.setPrimitiveParam(param, invokeData);
                    } else {
                        if (entity.fields) {
                            entity.fields.forEach(field => {
                                InvokeUtils.setPrimitiveParam(field, invokeData);
                            });
                        }
                    }
                } else if (param.annotation === '@RequestHeader') {
                    let paramName = param.name;
                    if (param.annotationParams && param.annotationParams['value']) {
                        paramName = param.annotationParams['value'];
                    }

                    let paramValue = param.demo;
                    let match = false;
                    if (!paramValue) {
                        // inngke定制开发
                        if (paramName === 'bid') {
                            paramValue = '1';
                            match = true;
                        } else if (paramName === 'operator-id') {
                            paramValue = '0';
                            match = true;
                        }
                    }

                    const headerParam: RequestParam = <RequestParam>{
                        key: paramName,
                        required: param.required,
                        value: paramValue,
                        description: param.comment,
                        match
                    };

                    if (param.annotationParams && param.annotationParams.length > 2) {
                        const params = JSON.parse(param.annotationParams);
                        if (params['value']) {
                            headerParam.key = params['value'];
                        }
                    }

                    const emptyParam = invokeData.headers.pop();
                    invokeData.headers.push(headerParam);
                    invokeData.headers.push(emptyParam);
                }
            });
        }

        InvokeUtils.addEmptyParamRow(invokeData.params);
        this.data = invokeData;
        this.setUrl();
        this.loadDemos();
    }

    setTab(tabName: string) {
        this.activeTab = tabName;
    }

    selectEnv(envs: EnvItem[]) {
        this.data.env = envs[0];
        this.setUrl();
    }

    selectMethod(methods: string[]) {
        this.data.method = methods[0];
        if (this.data.method !== 'POST' && this.activeTab === 'body') {
            this.activeTab = 'params';
        }
    }

    setUrl(): void {
        if (!this.data.env) {
            this.messageService.add(
                AlertMessage.error('RESTful环境未配置，请先设置环境！', 'rest-env-not-set'));
            return;
        }
        let url = this.data.env.url + this.api.paths[0];
        if (this.data.pathParams.length > 0) {
            // 尝试进行替换
            this.data.pathParams.forEach(pathParam => {
                if (pathParam.value) {
                    url = url.replace(pathParam.regex, pathParam.value);
                }
            });
        }
        this.data.url = url;
    }

    /**
     * 获取左侧菜单路由后缀
     * @returns {string[]}
     */


    /**
     * 获取左侧菜单路由后缀
     * @returns {string[]}
     */
    getLeftMenuRoutes(menu: Menu, page: PageInvokeComponent): string[] {
        if (menu.menuType === 0) {
            // RESTful
            return ['call'];
        } else if (menu.menuType === 1) {
            // Dubbo
            return ['dubbo'];
        }
        return null;
    }

    protected getLeftMenuId() {
        return this.apiId + ':call';
    }

    /**
     * 当前选中的左菜单
     * @returns {string}
     */
    protected getActiveMenuId(): string {
        return this.apiId;
    }

    act(event: string) {
        this.actionName = event[0];
        if (this.actionName === 'Send') {
            this.invokeResult = null;
            const obj: Observable<BaseResponse<InvokeResultDto>> = this.sendService.send(this.data);
            if (obj) {
                obj.subscribe(resp => {
                    this.invokeResult = resp.data;
                });
            }
        }
    }

    /**
     * 点击保存接口参数
     */
    clickSaveInvokeData(event: string[]) {
        if (this.sendService.checkInvokeData(this.data)) {
            this.editedData = {
                id: this.data.id,
                name: this.data.name,
                publicState: this.data.publicState
            };
            this.showSaveDialog = true;
        }
    }

    deleteInvokeConf() {
        if (!this.data.id) {
            return;
        }
        this.httpService.del(`/api/invoke/${this.data.id}`).subscribe((result: BaseResponse<boolean>) => {
            if (result.code === 200) {
                if (ArrayUtils.remove(this.demos, this.data)) {
                    this.data = this.demos[0];
                } else {
                    this.messageService.add(AlertMessage.error('删除失败！', 'del_demo_' + this.data.id));
                }
            }
        });
    }

    /**
     * 保存调用参数
     */
    saveInvokeData(event: string[]) {
        if (!this.editedData.name) {
            this.messageService.add(AlertMessage.error('接口调用参数名称不能为空！', 'invoke-data-save-name-error'));
            return;
        }
        const action = event[0];
        if (action === '另存为') {
            this.editedData.id = null;
        }

        const savedData = deepCopy(this.data);
        savedData.id = this.editedData.id;
        savedData.publicState = this.editedData.publicState;
        savedData.name = this.editedData.name;
        this.sendService.save(savedData).subscribe((response: BaseResponse<InvokeDataRest>) => {
            const editedDataId = this.editedData.id;
            if (response && response.code === 200) {
                // 保存成功
                this.messageService.add(AlertMessage.success('接口调用参数保存成功！', 'invoke-data-save-success'));
                const invokeData = response.data;
                // 重新设置env
                this.env.restEnvs.forEach(env => {
                    if (env.code === this.data.env.code) {
                        invokeData.env = env;
                    }
                });

                if (savedData.id) {
                    // 如果是修改
                    this.data.id = this.editedData.id;
                    this.data.publicState = this.editedData.publicState;
                    this.data.name = this.editedData.name;
                } else {
                    // 如果是新增
                    this.demos.push(invokeData);
                }

                this.showSaveDialog = false;

                // 添加到当前demos
                this.setInvokeData(invokeData);
            }
        });
    }

    selectDemo(demos: InvokeDataRest[]) {
        const newInvokeData = demos[0];
        this.setInvokeData(newInvokeData);
    }

    private setInvokeData(newInvokeData: InvokeDataRest) {
        this.invokeResult = null;
        this.initParamMap();
        const preData = this.data;
        if (newInvokeData.pathParams) {
            newInvokeData.pathParams.forEach(pathParam => InvokeUtils.setPathParamPattern(pathParam, this.api.paths[0]));
        }

        this.data = newInvokeData;

        // 触发参数检查
        this.completeParams('path', this.data.pathParams);
        this.completeParams('url', this.data.params);
        this.completeParams('header', this.data.headers);
        if (this.data.requestBody && this.data.requestBody.formData) {
            this.completeParams('form', this.data.requestBody.formData);
        }
    }

    private completeParams(type: string, params: RequestParam[]) {
        if (!params) {
            return;
        }
        params.forEach(param => {
            this.completeParam(type, param);
        });
    }

    private completeParam(type: string, param: RequestParam): void {
        if (!param.key) {
            param.match = true;
            return;
        }
        const key = type + ':' + param.key;
        const p = this.paramMap.get(key);
        if (!p) {
            param.match = true;
            return;
        }
        param.rulePattern = p.rulePattern;
        param.regex = p.regex;

        // 检查值
        if (param.rulePattern) {
            param.match = param.rulePattern.test(param.value);
        } else {
            param.match = true;
        }
    }

    private initParamMap() {
        if (this.paramMap) {
            return;
        }
        this.paramMap = new Map();
        if (this.data.pathParams) {
            this.data.pathParams.forEach(param => {
                if (!param.key) {
                    return;
                }
                this.paramMap.set('url:' + param.key, param);
            });
        }
        if (this.data.params) {
            this.data.params.forEach(param => {
                if (!param.key) {
                    return;
                }
                this.paramMap.set('url:' + param.key, param);
            });
        }
        if (this.data.headers) {
            this.data.headers.forEach(param => {
                if (!param.key) {
                    return;
                }
                this.paramMap.set('header:' + param.key, param);
            });
        }
        if (this.data.requestBody && this.data.requestBody.formData) {
            this.data.requestBody.formData.forEach(param => {
                if (!param.key) {
                    return;
                }
                this.paramMap.set('form:' + param.key, param);
            });
        }
    }

    private loadDemos() {
        this.httpService.get('/api/invoke/list/' + this.api.key).subscribe((response: BaseResponse<InvokeDataRest[]>) => {
            const demoList: InvokeDataRest[] = [this.data];
            if (response && response.code === 200 && response.data) {
                response.data.forEach(item => {
                    item.docId = this.docId;
                    item.env = this.getEnv(item['envCode']);
                    demoList.push(item);
                });
            }
            this.demos = demoList;
        });
    }

    private getEnv(envCode: string): EnvItem {
        if (!this.env.restEnvs) {
            return;
        }

        if (!envCode) {
            return this.env.restEnvs[0];
        }
        let env = null;
        this.env.restEnvs.forEach(item => {
            if (!env && item.code === envCode) {
                env = item;
            }
        });
        if (!env) {
            return this.env.restEnvs[0];
        }
        return env;
    }
}
